package cn.felord.common.kogito.dmn;

import org.kie.api.io.Resource;
import org.kie.dmn.api.core.DMNCompiler;
import org.kie.dmn.api.core.DMNContext;
import org.kie.dmn.api.core.DMNModel;
import org.kie.dmn.api.core.DMNResult;
import org.kie.dmn.core.api.DMNFactory;
import org.kie.kogito.Application;
import org.kie.kogito.decision.DecisionModel;
import org.kie.kogito.decision.DecisionModels;
import org.kie.kogito.dmn.rest.DMNJSONUtils;
import org.kie.kogito.dmn.rest.KogitoDMNResult;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.util.Assert;

import java.util.Map;

/**
 * The type Kogito application context.
 *
 * @author dax
 * @since 2023 /12/30
 */
public class DmnApplicationContext implements ApplicationContextAware {

    /**
     * The constant DMN_APPLICATION_BEAN_NAME.
     */
    public static final String DMN_APPLICATION_BEAN_NAME = "dmnApplication";
    private static final  DMNCompiler DMN_COMPILER = DMNFactory.newCompiler();
    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }


    /**
     * Evaluate decision
     *
     * @param namespace the namespace
     * @param modelName the model name
     * @param inputData the inputData
     * @return the kogito dmn result
     */
    public final KogitoDMNResult evaluate(String namespace, String modelName, Map<String, Object> inputData) {
        Assert.hasLength(namespace, "namespace must not be empty");
        Assert.hasLength(modelName, "modelName must not be empty");
        Assert.notEmpty(inputData, "inputData must not be empty");
        DecisionModel decision = this.getApplication()
                .get(DecisionModels.class)
                .getDecisionModel(namespace, modelName);
        DMNContext dmnContext = DMNJSONUtils.ctx(decision, inputData);
        DMNResult decisionResult = decision.evaluateAll(dmnContext);
        return new KogitoDMNResult(namespace, modelName, decisionResult);
    }

    /**
     * Evaluate kogito dmn result.
     *
     * @param model     the model
     * @param inputData the input data
     * @return the kogito dmn result
     */
    public final KogitoDMNResult evaluate(Resource model, Map<String, Object> inputData) {
        Assert.notNull(model, "dmnModel must not be null");
        Assert.notEmpty(inputData, "inputData must not be empty");
        DMNModel dmnModel = DMN_COMPILER.compile(model);
        DMNResult decisionResult = this.getApplication()
                .get(SimpleDecisionModels.class)
                .evaluate(dmnModel, inputData);
        return new KogitoDMNResult(dmnModel.getNamespace(), dmnModel.getName(), decisionResult);
    }


    /**
     * Refresh application.
     */
    public void refreshApplication() {
        ConfigurableApplicationContext ctx = (ConfigurableApplicationContext) applicationContext;
        DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) ctx.getBeanFactory();
        defaultListableBeanFactory.destroySingleton(DMN_APPLICATION_BEAN_NAME);
    }


    /**
     * Gets application.
     *
     * @return the application
     */
    public Application getApplication() {
        Application application = applicationContext.getBean(Application.class);
        Assert.notNull(application, "Kogito Application Bean Not Found");
        return application;
    }
}
